/* ===========================================================
 * trumbowyg.js v1.0
 * Core code of Trumbowyg plugin
 * http://alex-d.github.com/Trumbowyg
 * ===========================================================
 * Author : Alexandre Demode (Alex-D)
 *          Twitter : @AlexandreDemode
 *          Website : alex-d.fr
 */

$.trumbowyg = {

        langs: {
            en: {
                viewHTML: "View HTML",
                formatting: "Formatting",
                p: "Paragraph",
                blockquote: "Quote",
                code: "Code",
                header: "Header",
                bold: "Bold",
                italic: "Italic",
                strikethrough: "Stroke",
                underline: "Underline",
                strong: "Strong",
                em: "Emphasis",
                del: "Deleted",
                unorderedList: "Unordered list",
                orderedList: "Ordered list",
                insertImage: "Insert Image",
                insertVideo: "Insert Video",
                link: "Link",
                createLink: "Insert link",
                unlink: "Remove link",
                justifyLeft: "Align Left",
                justifyCenter: "Align Center",
                justifyRight: "Align Right",
                justifyFull: "Align Justify",
                horizontalRule: "Insert horizontal rule",
                fullscreen: "fullscreen",
                close: "Close",
                submit: "Confirm",
                reset: "Cancel",
                invalidUrl: "Invalid URL",
                required: "Required",
                description: "Description",
                title: "Title",
                text: "Text"
            }
        },
        opts: {},
        btnsGrps: {
            design: ["bold", "italic", "underline", "strikethrough"],
            semantic: ["strong", "em", "del"],
            justify: ["justifyLeft", "justifyCenter", "justifyRight", "justifyFull"],
            lists: ["unorderedList", "orderedList"]
        }
    },
    function (t) {
        t.fn.trumbowyg = function (i, n) {
            if (t.isObject(i) || null == i) return this.each(function () {
                t(this).data("trumbowyg") || t(this).data("trumbowyg", new e(this, i))
            });
            if (1 == this.length) try {
                var s = t(this).data("trumbowyg");
                switch (i) {
                    case "openModal":
                        return s.openModal(n.title, n.content);
                    case "closeModal":
                        return s.closeModal();
                    case "openModalInsert":
                        return s.openModalInsert(n.title, n.fields, n.callback);
                    case "saveSelection":
                        return s.saveSelection();
                    case "getSelection":
                        return s.selection;
                    case "getSelectedText":
                        return s.selection + "";
                    case "restoreSelection":
                        return s.restoreSelection();
                    case "destroy":
                        return s.destroy();
                    case "empty":
                        return s.empty();
                    case "lang":
                        return s.lang;
                    case "duration":
                        return s.o.duration;
                    case "html":
                        return s.html(n)
                }
            } catch (o) {}
            return !1
        };
        var e = function (e, i) {
            this.$e = t(e), this.$creator = t(e), i = t.extend(!0, {}, i, t.trumbowyg.opts), this.lang = "undefined" == typeof i.lang || "undefined" == typeof t.trumbowyg.langs[i.lang] ? t.trumbowyg.langs.en : t.extend(!0, {}, t.trumbowyg.langs.en, t.trumbowyg.langs[i.lang]), this.o = t.extend(!0, {
                lang: "en",
                dir: "ltr",
                duration: 200,
                mobile: !1,
                tablet: !0,
                closable: !1,
                fullscreenable: !0,
                fixedBtnPane: !1,
                fixedFullWidth: !1,
                semantic: !1,
                resetCss: !1,
                autogrow: !1,
                prefix: "trumbowyg-",
                convertLink: !0,
                btns: ["viewHTML", "|", "formatting", "|", t.trumbowyg.btnsGrps.design, "|", "link", "|", "insertImage", "|", t.trumbowyg.btnsGrps.justify, "|", t.trumbowyg.btnsGrps.lists, "|", "horizontalRule"],
                btnsAdd: [],
                btnsDef: {
                    viewHTML: {
                        func: "toggle"
                    },
                    p: {
                        func: "formatBlock"
                    },
                    blockquote: {
                        func: "formatBlock"
                    },
                    h1: {
                        func: "formatBlock",
                        title: this.lang.header + " 1"
                    },
                    h2: {
                        func: "formatBlock",
                        title: this.lang.header + " 2"
                    },
                    h3: {
                        func: "formatBlock",
                        title: this.lang.header + " 3"
                    },
                    h4: {
                        func: "formatBlock",
                        title: this.lang.header + " 4"
                    },
                    bold: {},
                    italic: {},
                    underline: {},
                    strikethrough: {},
                    strong: {
                        func: "bold"
                    },
                    em: {
                        func: "italic"
                    },
                    del: {
                        func: "strikethrough"
                    },
                    createLink: {},
                    unlink: {},
                    insertImage: {},
                    justifyLeft: {},
                    justifyCenter: {},
                    justifyRight: {},
                    justifyFull: {},
                    unorderedList: {
                        func: "insertUnorderedList"
                    },
                    orderedList: {
                        func: "insertOrderedList"
                    },
                    horizontalRule: {
                        func: "insertHorizontalRule"
                    },
                    formatting: {
                        dropdown: ["p", "blockquote", "h1", "h2", "h3", "h4"]
                    },
                    link: {
                        dropdown: ["createLink", "unlink"]
                    }
                }
            }, i), this.o.semantic && !i.btns ? this.o.btns = ["viewHTML", "|", "formatting", "|", t.trumbowyg.btnsGrps.semantic, "|", "link", "|", "insertImage", "|", t.trumbowyg.btnsGrps.justify, "|", t.trumbowyg.btnsGrps.lists, "|", "horizontalRule"] : i && i.btns && (this.o.btns = i.btns), this.init()
        };
        e.prototype = {
            init: function () {
                return this.height = this.$e.css("height"), this.isEnabled() ? (this.buildEditor(!0), void 0) : (this.buildEditor(), this.buildBtnPane(), this.fixedBtnPaneEvents(), this.buildOverlay(), void 0)
            },
            buildEditor: function (e) {
                if (e !== !0) {
                    this.$box = t("<div/>", {
                        "class": this.o.prefix + "box " + this.o.prefix + this.o.lang + " trumbowyg"
                    }), this.isTextarea = !0, this.$e.is("textarea") ? this.$editor = t("<div/>") : (this.$editor = this.$e, this.$e = this.buildTextarea().val(this.$e.val()), this.isTextarea = !1), this.$e.hide().addClass(this.o.prefix + "textarea");
                    var i = "";
                    this.isTextarea ? (i = this.$e.val(), this.$box.insertAfter(this.$e).append(this.$editor).append(this.$e)) : (i = this.$editor.html(), this.$box.insertAfter(this.$editor).append(this.$e).append(this.$editor), this.syncCode()), this.$editor.addClass(this.o.prefix + "editor").attr("contenteditable", !0).attr("dir", this.o.dir).html(i), this.o.resetCss && this.$editor.addClass(this.o.prefix + "reset-css"), this.o.autogrow || t.each([this.$editor, this.$e], t.proxy(function (t, e) {
                        e.css({
                            height: this.height,
                            overflow: "auto"
                        })
                    }, this)), this.o.semantic && (this.$editor.html(this.$editor.html().replace("<br>", "</p><p>").replace("&nsbp;", "")), this.semanticCode());
                    var n = this;
                    this.$editor.on("dblclick", "img", function () {
                        var e = t(this);
                        return n.openModalInsert(n.lang.insertImage, {
                            url: {
                                label: "URL",
                                value: e.attr("src"),
                                required: !0
                            },
                            alt: {
                                label: "description",
                                value: e.attr("alt")
                            }
                        }, function (t) {
                            e.attr("src", t.url), e.attr("alt", t.alt)
                        }), !1
                    }).on("keyup", function (t) {
                        n.semanticCode(!1, 13 === t.which)
                    }).on("blur", function () {
                        n.syncCode()
                    })
                } else if (!this.$e.is("textarea")) {
                    var s = this.buildTextarea().val(this.$e.val());
                    this.$e.hide().after(s)
                }
            },
            buildTextarea: function () {
                return t("<textarea/>", {
                    name: this.$e.attr("id"),
                    height: this.height
                })
            },
            buildBtnPane: function () {
                var e = this;
                if (e.o.btns !== !1) {
                    var i = e.o.prefix;
                    e.$btnPane = t("<ul/>", {
                        "class": i + "button-pane"
                    }), t.each(e.o.btns.concat(e.o.btnsAdd), t.proxy(function (n, s) {
                        try {
                            var o = s.split("btnGrp-");
                            void 0 != o[1] && (s = t.trumbowyg.btnsGrps[o[1]])
                        } catch (r) {}
                        t.isArray(s) || (s = [s]), t.each(s, t.proxy(function (n, s) {
                            try {
                                var o = t("<li/>");
                                "|" === s ? o.addClass(i + "separator") : e.isSupportedBtn(s) && ("viewHTML" === s && o.addClass(i + "not-disable"), o.append(e.buildBtn(s))), e.$btnPane.append(o)
                            } catch (r) {}
                        }, e))
                    }, e));
                    var n = t("<li/>", {
                        "class": i + "not-disable " + i + "buttons-right"
                    });
                    e.o.fullscreenable && n.append(e.buildRightBtn("fullscreen").on("click", t.proxy(function () {
                        var n = i + "fullscreen";
                        e.$box.toggleClass(n), e.$box.hasClass(n) ? (t("body").css("overflow", "hidden"), e.$box.css({
                            position: "fixed",
                            top: 0,
                            left: 0,
                            width: "100%",
                            height: "100%",
                            margin: 0,
                            padding: 0,
                            zIndex: 99999
                        }), t([e.$editor, e.$e]).each(function () {
                            t(this).css({
                                height: "100%",
                                overflow: "auto"
                            })
                        }), e.$btnPane.css("width", "100%")) : (t("body").css("overflow", "auto"), e.$box.removeAttr("style"), e.o.autogrow || (h = e.height, t([e.$editor, e.$e]).each(function (t, e) {
                            e.css("height", h)
                        }))), t(window).trigger("scroll")
                    }, e))), e.o.closable && n.append(e.buildRightBtn("close").on("click", t.proxy(function () {
                        var n = i + "fullscreen";
                        e.$box.hasClass(n) && t("body").css("overflow", "auto"), e.destroy()
                    }, e))), n.not(":empty") && e.$btnPane.append(n), e.$box.prepend(e.$btnPane)
                }
            },
            buildBtn: function (e) {
                var i = this.o.prefix,
                    n = this.o.btnsDef[e],
                    s = this,
                    o = this.lang[e] || e.charAt(0).toUpperCase() + e.slice(1),
                    r = t("<button/>", {
                        type: "button",
                        "class": i + e + "-button" + (n.ico ? " " + i + n.ico + "-button" : ""),
                        text: n.text || n.title || o,
                        title: n.title || n.text || o,
                        mousedown: function (o) {
                            return (!n.dropdown || s.$box.find("." + e + "-" + i + "dropdown").is(":hidden")) && t("body").trigger("mousedown"), s.$btnPane.hasClass(i + "disable") && !t(this).parent().hasClass(i + "not-disable") ? !1 : (s.execCommand((n.dropdown ? "dropdown" : !1) || n.func || e, n.param || e), o.stopPropagation(), o.preventDefault(), !1)
                        }
                    });
                if (n.dropdown) {
                    r.addClass(i + "open-dropdown");
                    for (var a = i + "dropdown", l = t("<div/>", {
                            "class": e + "-" + a + " " + a + " " + i + "fixed-top"
                        }), h = 0, d = n.dropdown.length; d > h; h++) s.o.btnsDef[n.dropdown[h]] && s.isSupportedBtn(n.dropdown[h]) && l.append(s.buildSubBtn(n.dropdown[h]));
                    this.$box.append(l.hide())
                }
                return r
            },
            buildSubBtn: function (e) {
                var i = this.o.btnsDef[e];
                return t("<button/>", {
                    type: "button",
                    text: i.text || i.title || this.lang[e] || e,
                    mousedown: t.proxy(function (n) {
                        return t("body").trigger("mousedown"), this.execCommand(i.func || e, i.param || e), n.stopPropagation(), n.preventDefault(), !1
                    }, this)
                })
            },
            buildRightBtn: function (e) {
                return t("<button/>", {
                    type: "button",
                    "class": this.o.prefix + e + "-button",
                    title: this.lang[e],
                    text: this.lang[e]
                })
            },
            isSupportedBtn: function (t) {
                return "function" != typeof this.o.btnsDef[t].isSupported || this.o.btnsDef[t].isSupported()
            },
            buildOverlay: function () {
                return this.$overlay = t("<div/>", {
                    "class": this.o.prefix + "overlay"
                }).css({
                    top: this.$btnPane.outerHeight(),
                    height: parseInt(this.$editor.outerHeight()) + 1 + "px"
                }).appendTo(this.$box)
            },
            showOverlay: function () {
                t(window).trigger("scroll"), this.$overlay.fadeIn(this.o.duration), this.$box.addClass(this.o.prefix + "box-blur")
            },
            hideOverlay: function () {
                this.$overlay.fadeOut(this.o.duration / 4), this.$box.removeClass(this.o.prefix + "box-blur")
            },
            fixedBtnPaneEvents: function () {
                this.o.fixedBtnPane && (this.isFixed = !1, t(window).on("scroll", t.proxy(function () {
                    if (this.$box) {
                        this.syncCode();
                        var e = t(window).scrollTop(),
                            i = this.$box.offset().top + 1,
                            n = e - i > 0 && e - i - parseInt(this.height) < 0;
                        n ? (this.isFixed || (this.isFixed = !0, this.$btnPane.css({
                            position: "fixed",
                            top: 0,
                            left: this.o.fixedFullWidth ? "0" : "auto",
                            width: this.o.fixedFullWidth ? "100%" : parseInt(this.$box.css("width")) - 1 + "px",
                            zIndex: 7
                        }), this.$editor.css({
                            marginTop: this.$btnPane.css("height")
                        }), this.$e.css({
                            marginTop: this.$btnPane.css("height")
                        })), t("." + this.o.prefix + "fixed-top", this.$box).css({
                            position: this.o.fixedFullWidth ? "fixed" : "absolute",
                            top: this.o.fixedFullWidth ? this.$btnPane.outerHeight() : parseInt(this.$btnPane.outerHeight()) + (e - i) + "px",
                            zIndex: 15
                        })) : this.isFixed && (this.isFixed = !1, this.$btnPane.css({
                            position: "relative"
                        }), this.$editor.css({
                            marginTop: 0
                        }), this.$e.css({
                            marginTop: 0
                        }), t("." + this.o.prefix + "fixed-top", this.$box).css({
                            position: "absolute",
                            top: this.$btnPane.outerHeight()
                        }))
                    }
                }, this)))
            },
            destroy: function () {
                var t = this.html();
                this.isTextarea ? this.$box.after(this.$e.css({
                    height: this.height
                }).val(t).removeClass(this.o.prefix + "textarea").show()) : this.$box.after(this.$editor.css({
                    height: this.height
                }).removeClass(this.o.prefix + "editor").attr("contenteditable", !1).html(t).show()), this.$box.remove(), this.$creator.removeData("trumbowyg")
            },
            empty: function () {
                this.$e.val(""), this.syncCode(!0)
            },
            toggle: function () {
                this.semanticCode(!1, !0), this.$editor.toggle(), this.$e.toggle(), this.$btnPane.toggleClass(this.o.prefix + "disable"), this.$btnPane.find("." + this.o.prefix + "viewHTML-button").toggleClass(this.o.prefix + "active")
            },
            dropdown: function (e) {
                var i = this.o.prefix,
                    n = this.$box.find("." + e + "-" + i + "dropdown"),
                    s = this.$btnPane.find("." + i + e + "-button");
                n.is(":hidden") ? (s.addClass(this.o.prefix + "active"), n.css({
                    position: "absolute",
                    top: this.$btnPane.outerHeight(),
                    left: this.o.fixedFullWidth && this.isFixed ? s.offset().left + "px" : s.offset().left - this.$btnPane.offset().left + "px"
                }).show(), t(window).trigger("scroll"), t("body").on("mousedown", t.proxy(function () {
                    t("." + i + "dropdown").hide(), t("." + i + "active").removeClass(i + "active"), t("body").off("mousedown")
                }, this))) : t("body").trigger("mousedown")
            },
            html: function (t) {
                return t ? (this.$e.val(t), this.syncCode(!0), tbw) : this.$e.val()
            },
            syncCode: function (t) {
                !t && this.$editor.is(":visible") ? this.$e.val(this.$editor.html()) : this.$editor.html(this.$e.val()), this.o.autogrow && (this.height = this.$editor.css("height"), this.$e.css({
                    height: this.height
                }))
            },
            semanticCode: function (e, i) {
                this.syncCode(e), this.o.semantic && (this.semanticTag("b", "strong"), this.semanticTag("i", "em"), this.semanticTag("strike", "del"), i && (this.$editor.contents().filter(function () {
                    return 3 === this.nodeType && t.trim(this.nodeValue).length > 0
                }).wrap("<p></p>").end().filter("br").remove(), this.saveSelection(), this.semanticTag("div", "p"), this.restoreSelection()), this.$e.val(this.$editor.html()))
            },
            semanticTag: function (e, i) {
                t(e, this.$editor).each(function () {
                    t(this).replaceWith(function () {
                        return "<" + i + ">" + t(this).html() + "</" + i + ">"
                    })
                })
            },
            createLink: function () {
                var t = this;
                this.saveSelection(), this.openModalInsert(this.lang.createLink, {
                    url: {
                        label: "URL",
                        value: "http://",
                        required: !0
                    },
                    title: {
                        label: this.lang.title,
                        value: this.selection
                    },
                    text: {
                        label: this.lang.text,
                        value: this.selection
                    }
                }, function (e) {
                    return t.execCommand("createLink", e.url), !0
                })
            },
            insertImage: function () {
                var e = this;
                this.saveSelection(), this.openModalInsert(this.lang.insertImage, {
                    url: {
                        label: "URL",
                        value: "http://",
                        required: !0
                    },
                    alt: {
                        label: "description",
                        value: this.selection
                    }
                }, function (i) {
                    return e.execCommand("insertImage", i.url), t(['img[src="', i.url, '"]:not([alt])'].join(""), e.$box).attr("alt", i.alt), !0
                })
            },
            execCommand: function (t, e) {
                "dropdown" != t && this.$editor.focus();
                try {
                    this[t](e)
                } catch (i) {
                    try {
                        t(e, this)
                    } catch (i) {
                        this.$editor.focus(), "insertHorizontalRule" == t && (e = null), document.execCommand(t, !1, e)
                    }
                }
                this.syncCode()
            },
            formatBlock: function (e) {
                t.browser.msie && (e = "<" + e + ">"), document.execCommand("formatBlock", !1, e)
            },
            openModal: function (e, i) {
                var n = this.o.prefix;
                if (t("." + n + "modal-box", this.$box).size() > 0) return !1;
                this.saveSelection(), this.showOverlay(), this.$btnPane.addClass(n + "disable"), t("." + n + "not-disable", this.$btnPane).not("." + n + "buttons-right").removeClass(n + "not-disable").addClass(n + "not-disable-old");
                var s = t("<div/>", {
                    "class": n + "modal " + n + "fixed-top"
                }).css({
                    top: parseInt(this.$btnPane.css("height")) + 1 + "px"
                }).appendTo(this.$box);
                this.$overlay.one("click", function (t) {
                    t.preventDefault(), s.trigger(n + "cancel")
                }), $e = this.$editor, $form = t("<form/>", {
                    action: "javascript:void(null);",
                    html: i
                }).on("submit", function (t) {
                    t.preventDefault(), s.trigger(n + "confirm")
                }).on("reset", function (t) {
                    t.preventDefault(), s.trigger(n + "cancel")
                });
                var o = t("<div/>", {
                    "class": n + "modal-box",
                    html: $form
                }).css({
                    top: "-" + parseInt(this.$btnPane.outerHeight()) + "px",
                    opacity: 0
                }).appendTo(s).animate({
                    top: 0,
                    opacity: 1
                }, this.o.duration / 2);
                return t("<span/>", {
                    text: e,
                    "class": n + "modal-title"
                }).prependTo(o), o.find("input:first").focus(), this.buildModalBtn("submit", o), this.buildModalBtn("reset", o), t("body").trigger("scroll"), s
            },
            buildModalBtn: function (e, i) {
                return t("<button/>", {
                    "class": this.o.prefix + "modal-button " + this.o.prefix + "modal-" + e,
                    type: e,
                    text: this.lang[e] || e
                }).appendTo(i.find("form"))
            },
            closeModal: function () {
                var e = this.o.prefix;
                this.$btnPane.removeClass(e + "disable"), this.$overlay.off(), t("." + this.o.prefix + "not-disable-old", this.$btnPane).removeClass(e + "not-disable-old").addClass(e + "not-disable");
                var i = this,
                    n = t("." + e + "modal-box", this.$box);
                n.animate({
                    top: "-" + n.css("height")
                }, this.o.duration / 2, function () {
                    t(this).parent().remove(), i.hideOverlay()
                })
            },
            openModalInsert: function (e, i, n) {
                var s = "",
                    o = this.o.prefix;
                for (f in i) {
                    var r = i[f];
                    label = void 0 == r.label ? this.lang[f] ? this.lang[f] : f.charAt(0).toUpperCase() + f.slice(1) : this.lang[r.label] ? this.lang[r.label] : r.label, void 0 == r.name && (r.name = f), r.pattern || "url" != f || (r.pattern = /^(http|https):\/\/([\w~#!:.?+=&%@!\-\/]+)$/, r.patternError = this.lang.invalidUrl), s += '<label><input type="' + (r.type || "text") + '" name="' + r.name + '" value="' + (r.value || "") + '"><span class="' + o + 'input-infos"><span>' + label + "</span></span></label>"
                }
                var a = this.openModal(e, s),
                    l = this;
                return a.on(o + "confirm", function () {
                    var e = t(this).find("form"),
                        s = !0,
                        r = {};
                    for (f in i) {
                        var h = t('input[name="' + f + '"]', e);
                        r[f] = h.val(), !i[f].required || null != r[f] && void 0 != r[f] && "" != t.trim(r[f]) ? i[f].pattern && !i[f].pattern.test(r[f]) && (s = !1, l.addErrorOnModalField(h, i[f].patternError)) : (s = !1, l.addErrorOnModalField(h, l.lang.required))
                    }
                    s && (l.restoreSelection(), n(r, i) && (l.syncCode(), l.closeModal(), a.off(o + "confirm")))
                }).one(o + "cancel", function () {
                    a.off(o + "confirm"), l.closeModal(), l.restoreSelection()
                }), a
            },
            addErrorOnModalField: function (t, e) {
                var i = t.parent(),
                    n = this.o.prefix;
                i.addClass(n + "input-error"), t.on("change keyup", function () {
                    i.removeClass(n + "input-error")
                }), i.find("input+span").append('<span class="' + n + 'msg-error">' + e + "</span>")
            },
            saveSelection: function () {
                if (this.selection = null, window.getSelection) {
                    var t = window.getSelection();
                    t.getRangeAt && t.rangeCount && (this.selection = t.getRangeAt(0))
                } else document.selection && document.selection.createRange && (this.selection = document.selection.createRange())
            },
            restoreSelection: function () {
                if (range = this.selection)
                    if (window.getSelection) {
                        var t = window.getSelection();
                        t.removeAllRanges(), t.addRange(range)
                    } else document.selection && range.select && range.select()
            },
            isEnabled: function () {
                var t = "iPhone|iPod|Android|BlackBerry|WindowssPhone|ZuneWP7",
                    e = new RegExp("(iPad|webOS)"),
                    i = new RegExp("(" + t + ")");
                return this.o.tablet === !0 && e.test(navigator.userAgent) || this.o.mobile === !0 && i.test(navigator.userAgent)
            }
        };
        var i = Object.prototype.toString,
            n = Object.prototype.hasOwnProperty;
        t.isObject = function (t) {
            if ("[object Object]" !== i.call(t)) return !1;
            var e;
            for (e in t);
            return !e || n.call(t, e)
        }, t.isString = function (t) {
            return "string" == typeof t
        }
    }(jQuery);